﻿using ArcFaceSDK.Entity;
using ArcFaceSDK.SDKModels;
using ArcFaceSDK.Utils;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 图书管理系统.Utils
{
    internal class FaceTool
    {
        private long maxSize = 1024 * 1024 * 2;
        private int maxWidth = 1536;
        private int maxHeight = 1536;
        private int index = -1;
        private List<FaceFeature> ImageFeatureList = new List<FaceFeature>();
        private List<FaceFeature> MatchImageFeatureList = new List<FaceFeature>(); 
        /// <summary>
        /// 保存对比图片的列表
        /// </summary>
        private List<string> imagePathList = new List<string>();

        private bool CheckImage(string imagePath)
        {
            try
            {
                if (imagePath == null)
                {
                    MessageBox.Show("图片不存在，请确认后再导入");
                    return false;
                }
                try
                {
                    //判断图片是否正常，如将其他文件把后缀改为.jpg，这样就会报错
                    Image image = ImageUtil.ReadFromFile(imagePath);
                    if (image == null)
                    {
                        throw new ArgumentException(" image is null");
                    }
                    else
                    {
                        image.Dispose();
                    }
                }
                catch
                {
                    MessageBox.Show(string.Format("{0} 图片格式有问题，请确认后再导入", imagePath));
                    return false;
                }
                FileInfo fileCheck = new FileInfo(imagePath);
                if (!fileCheck.Exists)
                {
                    MessageBox.Show(string.Format("{0} 不存在", fileCheck.Name));
                    return false;
                }
                else if (fileCheck.Length > maxSize)
                {
                    MessageBox.Show(string.Format("{0} 图片大小超过2M，请压缩后再导入", fileCheck.Name));
                    return false;
                }
                else if (fileCheck.Length < 2)
                {
                    MessageBox.Show(string.Format("{0} 图像质量太小，请重新选择", fileCheck.Name));
                    return false;
                }
            }
            catch (Exception ex)
            {
                Utils.LogUtil.LogInfo(GetType(), ex);
            }
            return true;
        }

        private void CheckImageWidthAndHeight(ref Image image)
        {
            if (image == null)
            {
                return;
            }
            try
            {
                if (image.Width > maxWidth || image.Height > maxHeight)
                {
                    image = ImageUtil.ScaleImage(image, maxWidth, maxHeight);
                }
            }
            catch (Exception ex)
            {
                LogUtil.LogInfo(GetType(), ex);
            }
        }

        private List<string> Getimagepath_FromSql()
        {
            SqlConnection con = Utils.SQLTool.GetCon();
            con.Open();
            string sql = "select facepath from MemberInfo where facepath != '无'";
            SqlCommand cmd = new SqlCommand(sql, con);
            SqlDataReader reader = cmd.ExecuteReader();
            List<string> imagePathList = new List<string>();
            while (reader.Read())
            {
                imagePathList.Add(reader.GetString(0));
            }
            return imagePathList;
        }

        /// <summary>
        /// 读取SQL所有人脸特征信息
        /// </summary>
        public List<FaceFeature> ChooseMultiImg()
        {
            try
            {
                List<string> imagePathListTemp = new List<string>();
                imagePathListTemp = Getimagepath_FromSql();
                var numStart = imagePathList.Count;
                int isGoodImage = 0;

                //人脸检测以及提取人脸特征
                ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
                {
                    //人脸检测和剪裁
                    for (int i = 0; i < imagePathListTemp.Count; i++)
                    {
                        Image image = ImageUtil.ReadFromFile(imagePathListTemp[i]);
                        //校验图片宽高
                        CheckImage(imagePathListTemp[i]);
                        CheckImageWidthAndHeight(ref image);
                        if (image == null)
                        {
                            continue;
                        }
                        //调整图像宽度，需要宽度为4的倍数
                        if (image.Width % 4 != 0)
                        {
                            image = ImageUtil.ScaleImage(image, image.Width - (image.Width % 4), image.Height);
                        }
                        //提取特征判断
                        int featureCode = -1;
                        SingleFaceInfo singleFaceInfo = new SingleFaceInfo();
                        FaceFeature feature = FaceUtil.ExtractFeature(Utils.Engine.imageEngine, image, out singleFaceInfo, ref featureCode);
                        if (featureCode != 0)
                        {
                            MessageBox.Show("未检测到人脸！");
                            if (image != null)
                            {
                                image.Dispose();
                            }
                            continue;
                        }
                        //人脸检测
                        MultiFaceInfo multiFaceInfo;
                        int retCode = Utils.Engine.imageEngine.ASFDetectFacesEx(image, out multiFaceInfo);
                        //判断检测结果
                        if (retCode == 0 && multiFaceInfo.faceNum > 0)
                        {
                            //多人脸时，默认裁剪第一个人脸
                            imagePathList.Add(imagePathListTemp[i]);
                            MRECT rect = multiFaceInfo.faceRects[0];
                            image = ImageUtil.CutImage(image, rect.left, rect.top, rect.right, rect.bottom);
                        }
                        else
                        {
                            MessageBox.Show("未检测到人脸！");
                            if (image != null)
                            {
                                image.Dispose();
                            }
                            continue;
                        }

                        //显示人脸
                        if (image == null)
                        {
                            image = ImageUtil.ReadFromFile(imagePathListTemp[i]);
                            //校验图片宽高
                            CheckImageWidthAndHeight(ref image);
                        }
                        ImageFeatureList.Add(feature);
                        //MessageBox.Show(string.Format("已提取{0}号人脸特征值，[left:{1},right:{2},top:{3},bottom:{4},orient:{5}]", (numStart + isGoodImage), singleFaceInfo.faceRect.left, singleFaceInfo.faceRect.right, singleFaceInfo.faceRect.top, singleFaceInfo.faceRect.bottom, singleFaceInfo.faceOrient));
                        isGoodImage++;
                        if (image != null)
                        {
                            image.Dispose();
                        }
                    }
                }));
                MessageBox.Show("读取数据库人脸信息成功！");
                return ImageFeatureList;
            }
            catch (Exception ex)
            {
                LogUtil.LogInfo(GetType(), ex);
                return null;
            }
        }

        /// <summary>
        /// 获取摄像头当前图像特征信息
        /// </summary>
        public List<FaceFeature> ChooseImg(Image image)
        {
            try
            {
                //判断引擎是否初始化成功
                if (!Utils.Engine.imageEngine.GetEngineStatus())
                {
                    MessageBox.Show("请先初始化引擎!");
                    return null;
                }

                Image srcImage = image;
                //校验图片宽高
                CheckImageWidthAndHeight(ref srcImage);
                if (srcImage == null)
                {
                    MessageBox.Show("图像数据获取失败，请稍后重试!");
                    return null;
                }
                //调整图像宽度，需要宽度为4的倍数
                if (srcImage.Width % 4 != 0)
                {
                    srcImage = ImageUtil.ScaleImage(srcImage, srcImage.Width - (srcImage.Width % 4), srcImage.Height);
                }
                //人脸检测
                MultiFaceInfo multiFaceInfo;
                int retCode = Utils.Engine.imageEngine.ASFDetectFacesEx(srcImage, out multiFaceInfo);
                if (retCode != 0)
                {
                    MessageBox.Show("图像人脸检测失败，请稍后重试!");
                    return null;
                }
                if (multiFaceInfo.faceNum < 1)
                {
                    srcImage = ImageUtil.ScaleImage(srcImage, 640, 480);
                    MessageBox.Show("未检测到人脸！");
                    return null;
                }

                //年龄检测
                int retCode_Age = -1;
                AgeInfo ageInfo = FaceUtil.AgeEstimation(Utils.Engine.imageEngine, srcImage, multiFaceInfo, out retCode_Age);
                //性别检测
                int retCode_Gender = -1;
                GenderInfo genderInfo = FaceUtil.GenderEstimation(Utils.Engine.imageEngine, srcImage, multiFaceInfo, out retCode_Gender);
                //3DAngle检测
                int retCode_3DAngle = -1;
                Face3DAngle face3DAngleInfo = FaceUtil.Face3DAngleDetection(Utils.Engine.imageEngine, srcImage, multiFaceInfo, out retCode_3DAngle);

                MRECT[] mrectTemp = new MRECT[multiFaceInfo.faceNum];
                int[] ageTemp = new int[multiFaceInfo.faceNum];
                int[] genderTemp = new int[multiFaceInfo.faceNum];
                SingleFaceInfo singleFaceInfo;

                //标记出检测到的人脸
                for (int i = 0; i < multiFaceInfo.faceNum; i++)
                {
                    MRECT rect = multiFaceInfo.faceRects[i];
                    int orient = multiFaceInfo.faceOrients[i];
                    int age = 0;
                    //年龄检测
                    if (retCode_Age != 0)
                    {
                        MessageBox.Show(string.Format("年龄检测失败，返回{0}!", retCode_Age));
                    }
                    else
                    {
                        age = ageInfo.ageArray[i];
                    }
                    //性别检测
                    int gender = -1;
                    if (retCode_Gender != 0)
                    {
                        MessageBox.Show(string.Format("性别检测失败，返回{0}!", retCode_Gender));
                    }
                    else
                    {
                        gender = genderInfo.genderArray[i];
                    }
                    //3DAngle检测
                    int face3DStatus = -1;
                    float roll = 0f;
                    float pitch = 0f;
                    float yaw = 0f;
                    if (retCode_3DAngle != 0)
                    {
                        MessageBox.Show(string.Format("3DAngle检测失败，返回{0}!", retCode_3DAngle));
                    }
                    else
                    {
                        //角度状态 非0表示人脸不可信
                        face3DStatus = face3DAngleInfo.status[i];
                        //roll为侧倾角，pitch为俯仰角，yaw为偏航角
                        roll = face3DAngleInfo.roll[i];
                        pitch = face3DAngleInfo.pitch[i];
                        yaw = face3DAngleInfo.yaw[i];
                    }
                    //提取人脸特征
                    MatchImageFeatureList.Add(FaceUtil.ExtractFeature(Utils.Engine.imageEngine, srcImage, out singleFaceInfo, ref retCode, i));

                    mrectTemp[i] = rect;
                    ageTemp[i] = age;
                    genderTemp[i] = gender;
                    //MessageBox.Show(string.Format("{0} - 第{12}人脸坐标:[left:{1},top:{2},right:{3},bottom:{4},orient:{5},roll:{6},pitch:{7},yaw:{8},status:{11}] Age:{9} Gender:{10}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), rect.left, rect.top, rect.right, rect.bottom, orient, roll, pitch, yaw, age, (gender >= 0 ? gender.ToString() : ""), face3DStatus, i));
                }
                MessageBox.Show("读取摄像头人脸信息成功！");

                //获取缩放比例
                float scaleRate = ImageUtil.GetWidthAndHeight(srcImage.Width, srcImage.Height, 430, 340);
                //缩放图片
                srcImage = ImageUtil.ScaleImage(srcImage, 430, 340);
                //添加标记
                srcImage = ImageUtil.MarkRectAndString(srcImage, mrectTemp, ageTemp, genderTemp, 430, scaleRate, multiFaceInfo.faceNum);
                return MatchImageFeatureList;
            }
            catch (Exception ex)
            {
                LogUtil.LogInfo(GetType(), ex);
                return null;
            }
        }

        /// <summary>
        /// 与数据库人脸匹配
        /// </summary>
        public bool MatchSQL(List<FaceFeature> ImageFeatureList, List<FaceFeature> MatchImageFeatureList, Image image)
        {
            try
            {
                if (ImageFeatureList.Count == 0)
                {
                    MessageBox.Show("数据库人脸信息为空，请注册人脸!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }

                if (MatchImageFeatureList == null || MatchImageFeatureList.Count <= 0)
                {
                    if (image == null)
                    {
                        MessageBox.Show("摄像头读取失败!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else
                    {
                        MessageBox.Show("比对失败，未提取到特征值!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    return false;
                }
                for (int faceIndex = 0; faceIndex < MatchImageFeatureList.Count; faceIndex++)
                {
                    float compareSimilarity = 0f;
                    int compareNum = 0;
                    FaceFeature tempFaceFeature = MatchImageFeatureList[faceIndex];
                    if (tempFaceFeature.featureSize <= 0)
                    {
                        //MessageBox.Show(string.Format("比对人脸第{0}张人脸特征提取失败", faceIndex));
                        continue;
                    }
                    //MessageBox.Show(string.Format("开始匹配比对人脸第{0}张人脸", faceIndex));
                    for (int i = 0; i < ImageFeatureList.Count; i++)
                    {
                        FaceFeature feature = ImageFeatureList[i];
                        float similarity = 0f;
                        Utils.Engine.imageEngine.ASFFaceFeatureCompare(tempFaceFeature, feature, out similarity);
                        //增加异常值处理
                        if (similarity.ToString().IndexOf("E") > -1)
                        {
                            similarity = 0f;
                        }
                        //MessageBox.Show(string.Format("与人脸库{0}号比对结果:{1}", i, similarity));
                        if (similarity > compareSimilarity)
                        {
                            compareSimilarity = similarity;
                            compareNum = i;
                        }
                    }
                    if (compareSimilarity < 0.85)
                    {
                        continue;
                        //MessageBox.Show(string.Format("匹配结果:{0}号,比对结果:{1}", compareNum, compareSimilarity));
                    }
                    else
                    {
                        index = compareNum;
                    }
                }
                //MessageBox.Show(string.Format("比对结束，时间:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));
                if(index != -1)
                {
                    MessageBox.Show("匹配到数据库人脸！");
                    return true;
                }
                else
                {
                    MessageBox.Show("在数据库中的匹配度未达85%！请重试！");
                    return false;
                }
            }
            catch (Exception ex)
            {
                LogUtil.LogInfo(GetType(), ex);
                return false;
            }
        }

        public int GetIndex()
        {
            SqlConnection con = Utils.SQLTool.GetCon();
            con.Open();
            string sql = "select uid from MemberInfo where facepath != '无'";
            SqlCommand cmd = new SqlCommand(sql, con);
            SqlDataReader reader = cmd.ExecuteReader();
            List<string> imagePathList = new List<string>();
            while (reader.Read())
            {
                imagePathList.Add(reader.GetString(0));
            }
            return Convert.ToInt32(imagePathList[index]);
        }
    }
}
